﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Web;
using System.Net;

namespace CrmField.Plugins
{


    public class WNSClient : CrmField.Plugins.IWNSClient
    {



        public string PostToWns(string secret, string sid, string uri, string xml, string notificationType, string contentType)
        {
            Trace("Begin PostToWns: {0}/{1}", secret, sid);
            try
            {
                // You should cache this access token.
                Trace("Get Token");
                var accessToken = GetAccessToken(secret, sid);

                Trace("Content to Post : {0}",xml);
                byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);

                Trace("Preparing to Post to WNS");
                WebClient client = new WebClient();
                client.Headers.Add("X-WNS-Type", notificationType);
                client.Headers.Add("Content-Type", contentType);
                client.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));

                Trace("Send WNS Request : {0}",uri);
                var responseInBytes = client.UploadData(uri,contentInBytes);

                Trace("Retrieve WNS Response");
                return Encoding.UTF8.GetString(responseInBytes);
            }
            catch (WebException webException)
            {
                Trace("Exception catched");
                string exceptionDetails = webException.Response.Headers["WWW-Authenticate"];
                if (exceptionDetails != null && exceptionDetails.Contains("Token expired"))
                {
                    Trace("Access token expired");
                    GetAccessToken(secret, sid);

                    // We suggest that you implement a maximum retry policy.
                    return PostToWns(uri, xml, secret, sid, notificationType, contentType);
                }
                else
                {
                    throw webException;
                }
            }
            catch (System.Security.SecurityException ex)
            {
                Trace("Exception {0} \n Permission: {1} / {2} \n {3}", ex, ex.PermissionType, ex.PermissionState, ex.StackTrace);
                throw ex;
            }
        }

        //private OAuthToken GetOAuthTokenFromJson(string jsonString)
        //{
        //    using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
        //    {
        //        var ser = new DataContractJsonSerializer(typeof(OAuthToken));
        //        var oAuthToken = (OAuthToken)ser.ReadObject(ms);
        //        return oAuthToken;
        //    }
        //}

        private OAuthToken GetOAuthTokenFromJson(string jsonString)
        {
            Trace("Parsing OAuth Token: {0}", jsonString);

            jsonString = jsonString.TrimStart('{');
            jsonString = jsonString.TrimEnd('}');
            var pairs = jsonString.Split(new char[] { ':', ',' }, StringSplitOptions.RemoveEmptyEntries);
            var jsonDictionary = new Dictionary<string, string>();
            for (int i = 0; i < pairs.Length; i += 2)
            {
                jsonDictionary.Add(pairs[i].Trim('\"'), pairs[i + 1].Trim('\"'));
            }

            Trace("Parsed OAuth Token: {0}", jsonString);

            return new OAuthToken() { AccessToken = jsonDictionary["access_token"], TokenType = jsonDictionary["token_type"] };
        }



        private OAuthToken GetAccessToken(string secret, string sid)
        {
            Trace("Requesting Token");

            var urlEncodedSecret = Uri.EscapeDataString(secret);
            var urlEncodedSid = Uri.EscapeDataString(sid);

            var body =
              String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com", urlEncodedSid, urlEncodedSecret);

            Trace("Token Resquest : {0} ", body);

            string response;
            using (var client = new WebClient())
            {
                Trace("Sending Token Request");
                client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                response = client.UploadString("https://login.live.com/accesstoken.srf", body);
                Trace("Token Response: {0}", response);
            }
            return GetOAuthTokenFromJson(response);
        }

        public delegate void LogHandler(string message);

        public event LogHandler OnLog;

        private void Trace(string trace)
        {
            if (OnLog != null) OnLog(trace);
        }

        private void Trace(string format, params object[] args)
        {
            this.Trace(string.Format(format, args));
        }
    }


}
